import socket
import time
from multiprocessing import Process

import numpy as np
import win32com.client
import logging

import errorhandle
# Allows communication via COM interface
from tools.tools import basic_log_config

try:
    import win32com.client
except:
    print("You will first need to import the pywin32 extension")
    print("to get COM interface support.")
    print("Try http://sourceforge.net/projects/pywin32/files/ )?")
    input("\nPress Enter to Exit Program\n")
    exit()


class R60(Process):

    def __init__(self, use_center_and_span=int(0), power_level_dbm=float(5), f1_hz=int(1e9), f2_hz=int(6e9),
                 num_points=int(201), parameter='s11', formatD='POLar', caliFile=None, freq=0.025, bandwidth=0):

        # choose the instrument
        super().__init__()
        basic_log_config()
        self.instrument = 'RVNA'

        # choose frequency type, 0 for Start/Stop Frequency, 1 for Center/Span Frequency
        self.use_center_and_span = use_center_and_span

        # power level R系列不可选
        self.power_level_dbm = power_level_dbm

        # fstart=400e6 or center, as per above, in Hz
        self.f1_hz = f1_hz
        # fstop=600e6 or span, as per above, in Hz
        self.f2_hz = f2_hz

        # number of measurement points
        self.num_points = num_points

        # "S21", "S11", "S12", etc. R54/140 must use
        # "S11"; TR devices must use "S11" or "S21";
        #  Ports 3 and 4 available for S8081 only
        self.parameter = parameter

        # "mlog" or "phase" or"smith chart" and so in
        self.format = formatD

        # measurement interval
        self.time_per_iter_sec = float(0)

        # number of times to loop
        self.num_iter = int(2)

        # number of function iterations to store
        self.num_iter_to_store = int(0)

        self.caliFile = caliFile

        self.bandwidth = bandwidth

        self.freq = freq
        self.ready = False

        # self.init()
        # self.prepare_connection()

    def init(self):
        # Instantiate COM client
        self.prepare_connection()
        try:
            self.app = win32com.client.Dispatch(self.instrument + ".application")
        except:
            self.ready = False
            errInfo = "Error establishing COM server connection to " + self.instrument + "."
            errInfo = errInfo + '\n' + "Check that the VNA application COM server was registered"
            errInfo.join(" at the time of software installation.")
            errInfo.join("This is described in the VNA programming manual.")
            logging.info(errInfo)
            return errInfo, errorhandle.VNA_CONNECTION_EXCEPTION

        # Wait up to 20 seconds for instrument to be ready
        if self.app.Ready == 0:
            logging.info("Instrument not ready! Waiting...")
            for k in range(1, 21):
                time.sleep(1)
                if self.app.Ready != 0:
                    break
                logging.info("%d" % k)

        # If the software is still not ready, cancel the program
        if self.app.Ready == 0:
            self.ready = False
            errInfo = "Error, timeout waiting for instrument to be ready.\n"
            errInfo.join("Check that VNA is powered on and connected to PC.\n")
            errInfo.join("The status Ready should appear in the lower right")
            errInfo.join(" corner of the VNA application window.\n")
            logging.error(errInfo)
            return errInfo, errorhandle.VNA_CONNECTION_EXCEPTION
        else:
            self.ready = False
            logging.info("Instrument ready! Continuing...")

        # Get and echo the instrument name, serial number, etc.
        #
        #  [This is a simple example of getting an ActiveX property in Python]
        #
        logging.info(self.app.name)
        self.app.scpi.system.preset()
        self.app.SCPI.MMEMory.LOAD.CALibration = self.caliFile
        self.app.scpi.GetSENSe(1).frequency.start = self.f1_hz
        self.app.scpi.GetSENSe(1).frequency.stop = self.f2_hz
        self.app.scpi.GetSENSe(1).sweep.points = self.num_points
        self.app.scpi.GetCALCulate(1).GetPARameter(1).define = self.parameter
        self.app.scpi.trigger.sequence.source = "INTernal"
        # app.scpi.trigger.sequence.source = "bus"
        self.app.scpi.GetCALCulate(1).GetPARameter(1).select()
        self.app.scpi.GetCALCulate(1).selected.format = self.format
        self.app.scpi.GetSENSe(1).BANDwidth.RESolution = self.bandwidth
        # Sets the instrument to a preset state
        #
        #  [This is an example of executing an ActiveX "method" in Python]
        #
        self.F = self.app.scpi.GetSENSe(1).frequency.data

        self.ready = True
        return 0

    def prepare_connection(self):
        self.udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        # 定义地址
        self.udp_socket.connect(('127.0.0.1', 9009))

    def setF(self):
        self.udp_socket.send(np.array(self.F[0:2]).tobytes())
        res = self.udp_socket.recv(10)
        if res == b'OK':
            return True
        else:
            return False

    def run(self):
        exceptCount = 0
        if self.init() == 0:
            while True:
                isOk = self.setF()
                if isOk:
                    break
        else:
            logging.error('Exception in VNA While connect VNA APP!')
            self.udp_socket.send(np.array([-1.0]).tobytes())
            # self.udp_socket.close()

        while self.ready:
            try:
                s11 = self.app.scpi.GetCALCulate(1).selected.data.Fdata
                s11 = np.array(s11)
                self.udp_socket.send(s11.tobytes())
                time.sleep(self.freq)
            except Exception as e:
                logging.error('Exception in VNA!' + str(e))
                if exceptCount <= 3:
                    exceptCount += 1
                    continue
                s11 = np.array([-1.0])
                self.udp_socket.send(s11.tobytes())
                # self.udp_socket.close()
                break

    def stop(self):
        self.ready = False
        self.terminate()